/**
 * @Author       : Mo 2427995270@qq.com
 * @Date         : 2025-08-14 18:14:32
 * @LastEditTime : 2025-08-17 07:07:28
 * @FilePath     : \ZN-1000-APP\libs\moblota.c
 * @Description  : mobl ota协议
 * @Version      : V1.0
 * @History      :
 * @Note         :
 */
/***************************************Includes***********************************/
#include "moblota.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if (USE_OS == OS_NONE) || (USE_OS == OS_FREERTOS)
#include "hal_delay.h"
#include "ll_crc.h"
#endif

#if USE_OS == OS_FREERTOS
#include "FreeRTOS.h"
#include "task.h"
#elif USE_OS == OS_PTHREAD
#include "log.h"
#include "serial_port.h"
#include <unistd.h>
#endif

/************************************Private Macros********************************/
// #define
#if USE_OS == OS_NONE
#define Delay_ms(ms) delay_ms_not_os(ms)
#elif USE_OS == OS_FREERTOS
#define Delay_ms(ms) delay_ms_os(ms)
#else
#define Delay_ms(ms) usleep(ms * 1000)
#endif

#define CMD_ENTER_BOOT "mo:enterboot"
#define CMD_ENTER_BOOT_ACK "mo:bootok"
#define CMD_ENTER_APP "mo:enterapp"
#define CMD_ENTER_APP_ACK "mo:appok"
#define CMD_FILE_SIZE "mo:size"
#define CMD_FILE_END "mo:end"
#define CMD_GET_BOOTVER "mo:getbootver"
#define CMD_GET_APPVER "mo:getappver"
#define CMD_HEARTBEAT "mo:heartbeat"
#define CMD_ACK_OK "mo:ok"
#define CMD_ACK_FAIL "mo:fail"

/**********************************Private Statement*******************************/

static int moblota_send_cmd(moblota_t *mot, const char *cmd, const char *ack, uint16_t timeout);
static void moblota_ack_ok(moblota_t *mot);
static void moblota_ack_fail(moblota_t *mot);
static void moblota_ack_ver(moblota_t *mot, char ver[16]);
/***********************************Public Variables*******************************/



/**********************************Private Variables*******************************/



/**********************************Public Functions********************************/

/**
 * @brief mobl ota初始化
 * @param  mot              
 * @param  uart             串口句柄
 * @param  cb               事件回调
 * @param  boot             1固件 0app
 * @note null
 */
void moblota_init(moblota_t *mot, uart_hal_t *uart, moblota_event_cb_t cb, uint8_t boot) {
    mot->uart = uart;
    mot->cb = cb;
    mot->boot = boot;
    mot->timeout = 20;
}
/**
 * @brief 设置模式
 * @param  mot
 * @param  master           1主机 0从机
 * @note null
 */
void moblota_set_mode(moblota_t *mot, uint8_t master) {
    mot->master = master;
}
/**
 * @brief 设置固件版本
 * @param  mot              
 * @param  ver              "x.xxxx.xxxx"
 * @note 主机询问时需要
 */
void moblota_set_bootver(moblota_t *mot, char *ver) {
    strncpy(mot->bootver, ver, sizeof(mot->bootver) - 1);
}
/**
 * @brief 设置app版本
 * @param  mot              
 * @param  ver              "x.xxxx.xxxx"
 * @note 主机询问时需要
 */
void moblota_set_appver(moblota_t *mot, char *ver) {
    strncpy(mot->appver, ver, sizeof(mot->appver) - 1);
}
/**
 * @brief 设置超时 ms
 * @param  mot              
 * @param  timeout          
 * @note null
 */
void moblota_set_timeout(moblota_t *mot, uint16_t timeout) {
    mot->timeout = timeout;
}
/**
 * @brief 进入固件
 * @param  mot              
 * @return int 0成功 -1失败
 * @note null
 */
int moblota_enter_boot(moblota_t *mot) {
    return moblota_send_cmd(mot, CMD_ENTER_BOOT, CMD_ENTER_BOOT_ACK, mot->timeout);
}
/**
 * @brief 进入app
 * @param  mot              
 * @return int 0成功 -1失败
 * @note 仅取消传输时发送，不发，boot空闲10秒也会进入app
 */
int moblota_enter_app(moblota_t *mot) {
    return moblota_send_cmd(mot, CMD_ENTER_APP, CMD_ENTER_APP_ACK, mot->timeout);
}
/**
 * @brief 检验文件名
 * @param  mot              
 * @param  name             长度不超过17
 * @return int 0成功 -1失败
 * @note null
 */
int moblota_check_file_name(moblota_t *mot, const char *name) {
    if (strlen(name) > 17) {
        return -1;
    }
    char cmd[24];
    snprintf(cmd, sizeof(cmd) - 1, "mo:%s", name);
    return moblota_send_cmd(mot, cmd, CMD_ACK_OK, mot->timeout);
}
/**
 * @brief 校验文件大小
 * @param  mot              
 * @param  size             
 * @return int 0成功 -1失败
 * @note null
 */
int moblota_check_file_size(moblota_t *mot, uint32_t size) {
    mot->file_size = size;
    char cmd[24];
    snprintf(cmd, sizeof(cmd) - 1, CMD_FILE_SIZE "%u", size);
    return moblota_send_cmd(mot, cmd, CMD_ACK_OK, mot->timeout);
}
/**
 * @brief 传输数据
 * @param  mot              
 * @param  buf              数据
 * @param  len              数据长度 max 508
 * @return int 0成功 -1失败
 * @note null
 */
int moblota_transfer_data(moblota_t *mot, void *buf, int len) {
    if (len > 508) {
        return -1;
    }
    crc_ll_reset_crc();
    uint32_t crc = crc_ll_crc32(0XFFFFFFFFU, buf, len);
    for (uint8_t i = 0; i < 3; i++) {
        uart_hal_get_rxlen(mot->uart) = 0;
        uart_hal_putbuf(mot->uart, buf, len);
        uart_hal_putbuf(mot->uart, (void *)&crc, sizeof(crc));
        uint16_t timeout = mot->timeout;
        while (timeout--) {
            Delay_ms(1);
            if (uart_hal_get_rxlen(mot->uart)) {
                break;
            }
        }
        if (timeout == 0) {
            continue;
        }
        if (strcmp(CMD_ACK_OK, (void *)uart_hal_get_rxbuf(mot->uart)) == 0) {
            return 0;
        }
        continue;
    }
    return -1;
}
/**
 * @brief 传输结束
 * @param  mot              
 * @return int 0成功 -1失败
 * @note null
 */
int moblota_transfer_end(moblota_t *mot) {
    return moblota_send_cmd(mot, CMD_FILE_END, CMD_ACK_OK, mot->timeout);
}
/**
 * @brief 获取固件版本
 * @param  mot              
 * @param  bootver          
 * @return int 0成功 -1失败
 * @note 询问从机
 */
int moblota_get_bootver(moblota_t *mot, char bootver[16]) {
    int ret = -1;
    moblota_send_cmd(mot, CMD_GET_BOOTVER, "ver:", mot->timeout);
    char *head = strchr((void *)uart_hal_get_rxbuf(mot->uart), ':');
    if (head) {
        strncpy(bootver, head + 1, 16);
        ret = 0;
    }
    return ret;
}
/**
 * @brief 获取app版本
 * @param  mot              
 * @param  appver           
 * @return int 0成功 -1失败
 * @note 询问从机
 */
int moblota_get_appver(moblota_t *mot, char appver[16]) {
    int ret = -1;
    moblota_send_cmd(mot, CMD_GET_APPVER, "ver:", mot->timeout);
    char *head = strchr((void *)uart_hal_get_rxbuf(mot->uart), ':');
    if (head) {
        strncpy(appver, head + 1, 16);
        ret = 0;
    }
    return ret;
}
/**
 * @brief 发送心跳
 * @param  mot              
 * @return int 0成功 -1失败
 * @note null
 */
int moblota_heartbeat(moblota_t *mot) {
    return moblota_send_cmd(mot, CMD_HEARTBEAT, CMD_ACK_OK, mot->timeout);
}
/**
 * @brief 应答进入固件ok
 * @param  mot              
 * @note null
 */
void moblota_enter_bootok(moblota_t *mot) {
    uart_hal_puts(mot->uart, CMD_ENTER_BOOT_ACK);
}
/**
 * @brief 应答进入appok
 * @param  mot              
 * @note null
 */
void moblota_enter_appok(moblota_t *mot) {
    uart_hal_puts(mot->uart, CMD_ENTER_APP_ACK);
}

/**
 * @brief 解析接收到的数据
 * @param  mot              
 * @param  buf              数据
 * @param  len              数据长度
 * @note null
 */
void moblota_analysis(moblota_t *mot, void *buf, int len) {
    if (mot->master) {
        if (strncmp("mo:", buf, 3) == 0) {
            if (strncmp(CMD_ENTER_BOOT, buf, len) == 0) {
                mot->cb(mot, MOBLOTA_EVENT_ENTERBOOT, NULL, 0);
            } else if (strncmp(CMD_ENTER_APP, buf, len) == 0) {
                mot->cb(mot, MOBLOTA_EVENT_ENTERAPP, NULL, 0);
            } else if (strncmp(CMD_GET_BOOTVER, buf, len) == 0) {
                mot->cb(mot, MOBLOTA_EVENT_GETBOOTVER, NULL, 0);
                moblota_ack_ver(mot, mot->bootver);
            } else if (strncmp(CMD_GET_APPVER, buf, len) == 0) {
                mot->cb(mot, MOBLOTA_EVENT_GETAPPVER, NULL, 0);
                moblota_ack_ver(mot, mot->appver);
            }
        }
    } else {
        if (strncmp("mo:", buf, 3) == 0) {
            if (strncmp(CMD_ENTER_BOOT, buf, len) == 0) {
                mot->cb(mot, MOBLOTA_EVENT_ENTERBOOT, NULL, 0);
            } else if (strncmp(CMD_ENTER_APP, buf, len) == 0) {
                mot->cb(mot, MOBLOTA_EVENT_ENTERAPP, NULL, 0);
            } else if (strncmp(CMD_FILE_END, buf, len) == 0) {
                if (mot->cb(mot, MOBLOTA_EVENT_FILEEND, NULL, 0) < 0) {
                    moblota_ack_fail(mot);
                } else {
                    moblota_ack_ok(mot);
                }
            } else if (strncmp(CMD_GET_BOOTVER, buf, len) == 0) {
                mot->cb(mot, MOBLOTA_EVENT_GETBOOTVER, NULL, 0);
                moblota_ack_ver(mot, mot->bootver);
            } else if (strncmp(CMD_GET_APPVER, buf, len) == 0) {
                mot->cb(mot, MOBLOTA_EVENT_GETAPPVER, NULL, 0);
                moblota_ack_ver(mot, mot->appver);
            } else if (strncmp(CMD_HEARTBEAT, buf, len) == 0) {
                mot->cb(mot, MOBLOTA_EVENT_HEARBEAT, NULL, 0);
                moblota_ack_ok(mot);
            } else if (strncmp(CMD_FILE_SIZE, buf, strlen(CMD_FILE_SIZE)) == 0) {
                mot->file_size = atoll((const char *)buf + strlen(CMD_FILE_SIZE));
                if (mot->cb(mot, MOBLOTA_EVENT_FILESIZE, &mot->file_size, sizeof(mot->file_size)) < 0) {
                    moblota_ack_fail(mot);
                } else {
                    moblota_ack_ok(mot);
                }
            } else if (strncmp(".bin", (const char *)buf + len - 4, 5) == 0) {
                if(mot->cb(mot, MOBLOTA_EVENT_FILENAME, (char *)buf + 3, len - 3) < 0) {
                    moblota_ack_fail(mot);
                } else {
                    moblota_ack_ok(mot);
                }
            }
        } else {
            // if (len & 0x03) {
            //     return ;
            // }
            if(len < 8) {
                return ;
            }
            uint8_t *bytes = buf + len - 4;
            uint32_t data_crc = bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3]) << 24;
            crc_ll_reset_crc();
            uint32_t crc = crc_ll_crc32(0XFFFFFFFFU, buf, (len - 4) >> 2);
            if (crc == data_crc) {
                if (mot->cb(mot, MOBLOTA_EVENT_FILEDATA, buf, len - 4) < 0) {
                    moblota_ack_fail(mot);
                } else {
                    moblota_ack_ok(mot);
                }
            } else {
                moblota_ack_fail(mot);
            }
        }
    }
}

/**********************************Private Functions*******************************/

static int moblota_send_cmd(moblota_t *mot, const char *cmd, const char *ack, uint16_t timeout) {
    uart_hal_get_rxlen(mot->uart) = 0;
    uart_hal_puts(mot->uart, (void *)cmd);
    if (timeout == 0) {
        return 0;
    }
    while (timeout--) {
        Delay_ms(1);
        if (uart_hal_get_rxlen(mot->uart)) {
            break;
        }
    }
    if (timeout == 0) {
        return -1;
    }
    if (ack) {
        if (strcmp(ack, (void *)uart_hal_get_rxbuf(mot->uart)) == 0) {
            return 0;
        }
        return -1;
    }
    return 0;
}

/**
 * @brief 应答ok
 * @param  mot              
 * @note null
 */
static void moblota_ack_ok(moblota_t *mot) {
    uart_hal_puts(mot->uart, CMD_ACK_OK);
}
/**
 * @brief 应答失败
 * @param  mot              
 * @note null
 */
static void moblota_ack_fail(moblota_t *mot) {
    uart_hal_puts(mot->uart, CMD_ACK_FAIL);
}
/**
 * @brief 应答版本
 * @param  mot              
 * @param  ver              
 * @note null
 */
static void moblota_ack_ver(moblota_t *mot, char ver[16]) {
    char ack[48];
    snprintf(ack, sizeof(ack), "ver:%s", ver);
    uart_hal_puts(mot->uart, ack);
}

/* [] END OF FILE */
